package cn.likun.kit.file;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import cn.likun.kit.file.bean.FileBean;
import cn.likun.kit.file.exception.FileExistException;
import cn.likun.kit.file.support.AbstractFileFilter;
import cn.likun.kit.validate.ValidateCommons;

/**
 * <p>java.util.zip</p>
 * <p>打包解压缩zip</p>
 */
public class FileZipPackage {
	
	private FileZipPackage() {}
	private static final String SUFFIX_NAME = "zip" ;
	private static byte[] byteBuffer = new byte[1024];
	/**
	 * <p>压缩srcPath成zip文件</p>
	 * @param srcPath
	 * @param targetPath
	 * @param hasRoot 压缩包是否包含root文件夹
	 * @param root	      如果包含root文件夹 , 则此处root为空时,用zipName作为root文件夹
	 * @throws FileExistException
	 * @throws IOException
	 */
	public static void pack(String srcPath, String targetPath ,boolean hasRoot, String root) throws FileExistException, IOException {
		String targetPathPatern ;
		if (FileCommons.getLastPathNode(targetPath).indexOf('.') > -1) {
			targetPathPatern = FileCommons.cutLastPathNode(targetPath);
		} else {
			targetPathPatern = FileCommons.changeCompatSeparator(targetPath);
		}
		String srcPathPatern = FileCommons.changeCompatSeparator(srcPath);
		if (targetPathPatern.contains(srcPathPatern) && targetPathPatern.length() > srcPathPatern.length()) {
			throw new RuntimeException("目标路径不可以是源文件夹的子路径..");
		}
		srcPath = FileCommons.changeSeparator(srcPath);
		targetPath = FileCommons.changeSeparator(targetPath);
		File srcFile = new File(srcPath);
		if (srcFile.exists()) {
			ZipOutputStream out = null ;
			try {
				String zipNameWithoutSuffix = null;
				String suffixName = FileCommons.getSuffixName(targetPath);
				String zipName = null;
				if (SUFFIX_NAME.equals(suffixName)) {
					zipName = FileCommons.cutLastPathNode(targetPath);
					zipNameWithoutSuffix = zipName.substring(0,zipName.lastIndexOf('.') - 1);
					FileCommons.createFolder(FileCommons.cutLastPathNode(targetPath));
				} else {
					FileCommons.createFolder(targetPath);
					zipNameWithoutSuffix = FileCommons.getLastPathNode(targetPath);
					zipName = new StringBuffer().append(zipNameWithoutSuffix).append(".").append(SUFFIX_NAME).toString();
					targetPath = new StringBuffer(targetPath).append(File.separator).append(zipName).toString();
				}
				File targetFile = new File(targetPath);
				if (targetFile.exists()) {
					throw new FileExistException(targetPath);
				}
				out = new ZipOutputStream(new FileOutputStream(targetFile));
				if (hasRoot) {
					if (ValidateCommons.isEmpty(root)) {
						root = "";
					}
					root = zipNameWithoutSuffix ;
				}
				String srcDestFolder = srcFile.getPath();
				if (srcFile.isDirectory()) {
					packFolder(srcFile, srcDestFolder, root, out, zipName , targetPathPatern.equals(srcPathPatern));
				} else {
					packFile(srcFile, srcDestFolder, root, out);
				}
			} finally {
				if (out != null) {
					out.flush();
					out.close();
				}
			}
		}
		
	}
	/**
	 * <p>解压</p>
	 * @param zipPath
	 * @param targetPath
	 * @throws IOException
	 * @throws FileExistException
	 */
	public static void unpack(String zipPath, String targetPath) throws IOException, FileExistException {
		zipPath = FileCommons.changeSeparator(zipPath);
		targetPath = FileCommons.changeSeparator(targetPath);
		ZipFile zipFile = null ;
		try {
			zipFile = new ZipFile(zipPath);
			Enumeration<? extends ZipEntry> entries = zipFile.entries();
			while (entries.hasMoreElements()) {
				ZipEntry zipEntry = entries.nextElement();
				String name = zipEntry.getName();
				String filePath = new StringBuffer(targetPath).append(File.separator).append(name).toString();
				if (name.endsWith(File.separator)) {
					FileCommons.createFolder(filePath);
				} else {
					FileCommons.createFile(zipFile.getInputStream(zipEntry), filePath);
				}
			}
		} finally {
			if (ValidateCommons.isNotEmpty(zipFile)) {
				zipFile.close();
			}
		}
	}
	
	/**
	 * zipFIle在src路径中要过滤掉 , 也要在子文件夹子文件夹下面过滤掉
	 * @param file
	 * @param srcDestFolder
	 * @param root
	 * @param out
	 * @param zipName
	 * @throws IOException
	 */
	private static void packFolder(File file ,String srcDestFolder, String root,ZipOutputStream out, final String zipName ,final boolean thorwZipName) throws IOException {
		String name = getEntryName(file, srcDestFolder);
		//如果此路径为原压缩目录的根目录,则不代表root
		if (ValidateCommons.isNotEmpty(name)) {
			name = FileCommons.changeCompatSeparator(root + File.separator + name )+"/";
			ZipEntry entry = new ZipEntry(name);
			out.putNextEntry(entry);
			out.closeEntry();
		}
		// 打包的时候,如果打包在当前文件夹下 ,不应该包括目标zip
		List<FileBean> fileBeans = FileCommons.listFolder(file.getPath(), null, new AbstractFileFilter(file.getPath()){
			@Override
			public boolean isThrowFile(FileBean fileBean) {
				if (thorwZipName) {
					return fileBean.getName().equals(zipName);
				} else {
					return false;
				}
			}
		});
		if (ValidateCommons.isNotEmpty(fileBeans)) {
			for (FileBean fileBean:fileBeans) {
				if (fileBean.getIsFolder()) {
					packFolder(FileCommons.fileBeanToFile(fileBean), srcDestFolder, root, out, zipName, thorwZipName);
				} else {
					packFile(FileCommons.fileBeanToFile(fileBean), srcDestFolder, root, out);
				}
			}
		}
		
	}
	
	private static void packFile(File file, String srcDestFolder, String root, ZipOutputStream out) throws IOException {
		BufferedInputStream bufIn = null;
		try {
			String name = getEntryName(file, srcDestFolder);
			root = FileCommons.changeSeparator(root);
			ZipEntry entry = new ZipEntry(root + File.separator + name);
			out.putNextEntry(entry);
			bufIn = new BufferedInputStream(new FileInputStream(file));
			int index = 0 ;
			while ((index = bufIn.read(byteBuffer)) != -1) {
				out.write(byteBuffer,0,index);
				out.flush();
			}
		} finally {
			byteBuffer = new byte[1024];
			if (bufIn != null) {
				bufIn.close();
			}
		}
	}

	private static String getEntryName(File file, String srcDestFolder) {
		String filePath = file.getPath();
		filePath = FileCommons.changeCompatSeparator(filePath);
		srcDestFolder = FileCommons.changeCompatSeparator(srcDestFolder);
		if (new File(srcDestFolder).isFile()) {
			return file.getName();
		}
		String name = "";
		if (filePath.equals(srcDestFolder)) {
			return name;
		}
		name = filePath.substring(srcDestFolder.length() + 1);
		return name;
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
}
